#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_MAGIC_COOKIE 0x63825363
#define DHCP_OFFER 2
#define DHCP_ACK 5
#define FIXED_IP "192.168.1.2"

struct dhcp_packet {
    uint8_t op;
    uint8_t htype;
    uint8_t hlen;
    uint8_t hops;
    uint32_t xid;
    uint16_t secs;
    uint16_t flags;
    uint32_t ciaddr;
    uint32_t yiaddr;
    uint32_t siaddr;
    uint32_t giaddr;
    uint8_t chaddr[16];
    uint8_t sname[64];
    uint8_t file[128];
    uint32_t magic_cookie;
    uint8_t options[312];
};

void send_dhcp_packet(int sockfd, struct dhcp_packet *packet, struct sockaddr_in *client_addr) {
    sendto(sockfd, packet, sizeof(struct dhcp_packet), 0, (struct sockaddr *)client_addr, sizeof(struct sockaddr_in));
}

void handle_dhcp_discover(int sockfd, struct dhcp_packet *discover_packet, struct sockaddr_in *client_addr) {
    struct dhcp_packet offer_packet;
    memset(&offer_packet, 0, sizeof(struct dhcp_packet));
    offer_packet.op = DHCP_OFFER;
    offer_packet.htype = discover_packet->htype;
    offer_packet.hlen = discover_packet->hlen;
    offer_packet.hops = discover_packet->hops;
    offer_packet.xid = discover_packet->xid;
    offer_packet.secs = discover_packet->secs;
    offer_packet.flags = discover_packet->flags;
    inet_pton(AF_INET, FIXED_IP, &offer_packet.yiaddr);
    memcpy(offer_packet.chaddr, discover_packet->chaddr, discover_packet->hlen);
    offer_packet.magic_cookie = DHCP_MAGIC_COOKIE;
    offer_packet.options[0] = 53;
    offer_packet.options[1] = 1;
    offer_packet.options[2] = DHCP_OFFER;
    send_dhcp_packet(sockfd, &offer_packet, client_addr);
}

void handle_dhcp_request(int sockfd, struct dhcp_packet *request_packet, struct sockaddr_in *client_addr) {
    struct dhcp_packet ack_packet;
    memset(&ack_packet, 0, sizeof(struct dhcp_packet));
    ack_packet.op = DHCP_ACK;
    ack_packet.htype = request_packet->htype;
    ack_packet.hlen = request_packet->hlen;
    ack_packet.hops = request_packet->hops;
    ack_packet.xid = request_packet->xid;
    ack_packet.secs = request_packet->secs;
    ack_packet.flags = request_packet->flags;
    inet_pton(AF_INET, FIXED_IP, &ack_packet.yiaddr);
    memcpy(ack_packet.chaddr, request_packet->chaddr, request_packet->hlen);
    ack_packet.magic_cookie = DHCP_MAGIC_COOKIE;
    ack_packet.options[0] = 53;
    ack_packet.options[1] = 1;
    ack_packet.options[2] = DHCP_ACK;
    send_dhcp_packet(sockfd, &ack_packet, client_addr);
}

int main() {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(DHCP_SERVER_PORT);
    bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    struct dhcp_packet recv_packet;
    while (1) {
        memset(&recv_packet, 0, sizeof(struct dhcp_packet));
        recvfrom(sockfd, &recv_packet, sizeof(struct dhcp_packet), 0, (struct sockaddr *)&client_addr, &client_addr_len);
        if (recv_packet.options[2] == 1) {
            handle_dhcp_discover(sockfd, &recv_packet, &client_addr);
        } else if (recv_packet.options[2] == 3) {
            handle_dhcp_request(sockfd, &recv_packet, &client_addr);
        }
    }
    close(sockfd);
    return 0;
}    